package org.pps.gl.server.datastore;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import org.pps.gl.client.data.NewsInfo;
import org.pps.gl.client.data.UserInfo;
import org.pps.gl.server.GetInfoServiceImpl;
import org.pps.gl.server.Email;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

/**
 * Cette classe prend en charge toutes les operations en rapport avec l'entitée
 * User.
 * 
 * Un User a les propriétés suivantes : - nom - prénom - dateNaissance - sexe -
 * profil (Key) - contacts (ArrayList<String>)
 * 
 */
public class Utilisateur {

	/**
	 * Cree un nouvel utilisateur.
	 * 
	 * @param email
	 *            : email de l'utilisateur
	 * @param nom
	 *            :
	 * @param prenom
	 * @param dateNaissance
	 * @param sexe
	 * @return updated user
	 */
	public static void createUser(String email, String password, String nom,
			String prenom, String dateNaissance, String sexe) {

		// Nous verifions si l'utilisateur exister deja ou non.
		Entity user = getUtilisateur(email);

		if (user == null) { // Si ce n'est pas le cas nous le créons.

			user = new Entity("Utilisateur", email); // Creation de l'entitée
														// pour l'utilisateur

			// Création du profil
			Entity profil = Profil.createOrUpdateProfil(user, email,
					"non renseigné");

			// Mise en place des differentes proprétés pour cet utilisateur
			user.setProperty("profil", profil.getKey());
			user.setProperty("contacts", new ArrayList<String>());
			user.setProperty("password", password);
			user.setProperty("nom", nom);
			user.setProperty("prenom", prenom);
			user.setProperty("dateNaissance", dateNaissance);
			user.setProperty("sexe", sexe);

			// Insertion dans le datastore de cette nouvelle entité
			Util.persistEntity(user);

			// on ajoute un evenement
			Entity event = EventLogger.createEventLogger(user, new Date(),
					EventLogger.EVENT_CREATE_PROFILE, new ArrayList<String>());
			user.setProperty(EventLogger.EVENT_LOGGER_KIND, event.getKey());
			Util.persistEntity(user);

			// envoi d'un mail confirmant l'inscription
			Email.sendMail(email, nom, prenom, password);

		}

		// else
		// ------------------- RENVOYER UNE EXCEPTION USER ALREADY EXIST
	}

	/**
	 * Méthode permettant de mettre à jour une propriété d'un utilisateur
	 * spécifique.
	 * 
	 * @param email
	 *            : email de l'utilisateur
	 * @param property
	 *            : propriété à mettre à jour (nom, prenom, dateNaissance, sexe)
	 * @param value
	 *            : valeur à insérer.
	 */
	public static void updateUser(String email, String property, String value) {
		// Nous recuperons l'utilisateur correspondant
		Entity user = getUtilisateur(email);

		// Mise à jour de la propriété
		user.setProperty(property, value);

		Util.persistEntity(user);

	}

	/**
	 * Retourne un itérateur sur tous les users.
	 * 
	 * @return Utilisateur(s)
	 */
	public static Iterable<Entity> getAllUtilisateur() {
		return Util.listEntities("Utilisateur", null, null);
	}

	/**
	 * Recupere l'entitee de l'utilisateur.
	 * 
	 * @param email
	 *            : email de l'utilisateur.
	 * @return: Utilisateur entity.
	 */
	public static Entity getUtilisateur(String email) {
		Key key = KeyFactory.createKey("Utilisateur", email);
		return Util.findEntity(key);
	}

	/**
	 * Retourne un profil correspondant à un utilisateur.
	 * 
	 * @param Utilisateur
	 *            : l'utilisateur concerne.
	 * @return profil : une entitée représentant le profil.
	 */
	public static Entity getProfil(Entity utilisateur) {
		// Key key = KeyFactory.createKey("Profil", (String)
		// utilisateur.getProperty("profil"));
		return Util.findEntity((Key) utilisateur.getProperty("profil"));
	}

	/**
	 * Méthode permettant de rajouter un contact à un utilisateur.
	 * 
	 * @param Utilisateur
	 *            : utilisateur auquel on veut ajouter un contact
	 * @param email
	 *            : String représentant l'adresse email du contact à ajouter.
	 */
	public static void addContact(Entity utilisateur, String email) {
		// On recupere la liste des contacts et on ajoute le nouveau
		Entity contact = new Entity("Contact", utilisateur.getKey());
		contact.setProperty("email", email);
		Util.persistEntity(contact);

	}

	/**
	 * Fonction qui permet de récupérer la liste des contacts d'un utilisateur
	 * 
	 * @param Utilisateur
	 *            : utilisateur concerné.
	 * @return un ArrayList contenant toutes les adresses email des contacts.
	 */
	public static ArrayList<String> getAllContacts(Entity utilisateur) {

		ArrayList<String> ret = new ArrayList<String>();
		for (Entity e : Util.listChildren("Contact", utilisateur.getKey())) {
			ret.add(e.getProperty("email").toString());
		}
		return ret;
	}

	/**
	 * Méthode qui permet de récupérer le nom de l'utilisateur.
	 * 
	 * @param user
	 * @return
	 */
	public static String getNom(Entity user) {
		return (String) user.getProperty("nom");
	}

	/**
	 * Méthode qui permet de récupérer le prénom de l'utilisateur.
	 * 
	 * @param user
	 * @return
	 */
	public static String getPrenom(Entity user) {
		return (String) user.getProperty("prenom");
	}

	public static String getDateNaissance(Entity user) {
		return (String) user.getProperty("dateNaissance");
	}

	public static String getSexe(Entity user) {
		return (String) user.getProperty("sexe");
	}

	/**
	 * Efface une entité utilisateur
	 * 
	 * @param utilisateurEmail
	 *            : product to be deleted
	 * @return status string
	 */
	public static String deleteUser(String utilisateurEmail) {
		Key key = KeyFactory.createKey("Utilisateur", utilisateurEmail);
		Entity utilisateur = getUtilisateur(utilisateurEmail);

		if (null != utilisateur) {
			/*
			 * On récupère le profil correspondant afin de l'effacer (avant
			 * d'effacer l'user en lui-même.)
			 */
			Entity profil = (Entity) Profil.getProfil(utilisateurEmail);

			if (null != profil)
				Profil.deleteProfil(profil);

			// On efface l'entité de l'user en dernier lieu
			Util.deleteEntity(key);

			return "Profil supprimé de l'utilisateur : " + utilisateurEmail;
		} else
			return null;

	}

	public static boolean checkLoginPassword(String email, String password) {
		Entity utilisateur = Utilisateur.getUtilisateur(email);
		if (utilisateur != null) {
			
			if (utilisateur.getProperty("password").equals(password)){
				return true;
			}
				
			else
				return false;
		} else
			return false;
	}

	public static void cleanSessionIDS() {
		Iterable<Entity> sids = Util.listEntities("SessionId", null, null);
		for (Entity sid : sids) {
			if (((Long) sid.getProperty("Expires")) < System
					.currentTimeMillis())
				Util.deleteEntity(sid.getKey());
		}
	}

	public static Entity getCurrentUser(String sessionID) {

		if (sessionID != null)
			return getUserBySessionId(sessionID);

		else
			return null;
	}

	public static boolean delSession(String sid) {
		boolean ret = false;
		for (Entity s : Util.listEntities("SessionId", null, null)) {
			if (s.getKey().getName().equals(sid)) {
				Util.deleteEntity(s.getKey());
				ret = true;
			}

		}
		return ret;
	}

	public static Entity getUserBySessionId(String id) {
		Entity sessionid = null;
		for (Entity s : Util.listEntities("SessionId", null, null)) {
			if (s.getKey().getName().equals(id)) {
				sessionid = s;
			}

		}

		if (sessionid == null)
			return null;
		if ((((Date) sessionid.getProperty("Expires"))).getTime() < System
				.currentTimeMillis())
			return null;
		System.out.println("On cherche "
				+ (String) sessionid.getProperty("email"));
		Entity user = getUtilisateur((String) sessionid.getProperty("email"));
		return user;
	}

	public static String newSessionId(String email) {
		Entity user = getUtilisateur(email);
		String sId = null;
		if (user != null) {
			sId = UUID.randomUUID().toString();
			Entity sessionid = new Entity("SessionId", sId, user.getKey()); // Creation
			// de l'entitée pour l'id de session
			sessionid.setProperty("email", email);
			final long DURATION = 1000 * 60 * 60 * 24 * 14; // duration
			Date expires = new Date(System.currentTimeMillis() + DURATION);

			sessionid.setProperty("Expires", expires);
			Util.persistEntity(sessionid);

		}

		return sId;
	}

	public static boolean createContactDemande(String emailDemandeur,
			String emailReceiver) {
		Entity demande = new Entity("Demande");
		demande.setProperty("demandeur", emailDemandeur);
		demande.setProperty("receveur", emailReceiver);
		Util.persistEntity(demande);
		return true;

	}

	public static ArrayList<Entity> getUserReceived(String email) {
		ArrayList<Entity> ret = new ArrayList<Entity>();
		for (Entity s : Util.listEntities("Demande", null, null)) {
			if (s.getProperty("receveur").toString().equals(email)) {
				ret.add(s);
			}

		}
		return ret;
	}

	public static ArrayList<Entity> getUserDemandes(String email) {
		ArrayList<Entity> ret = new ArrayList<Entity>();
		for (Entity s : Util.listEntities("Demande", null, null)) {
			if (s.getProperty("demandeur").toString().equals(email)) {
				ret.add(s);
			}

		}
		return ret;
	}

	public static boolean confirmContact(String emailDemandeur,
			String emailReceiver) {
		Entity demande = null;
		for (Entity s : getUserReceived(emailReceiver)) {
			if (s.getProperty("demandeur").toString()
					.equalsIgnoreCase(emailDemandeur))
				demande = s;
		}
		if (demande == null)
			return false;
		Entity demandeur = Utilisateur.getUtilisateur(emailDemandeur);
		Entity receiver = Utilisateur.getUtilisateur(emailReceiver);
		addContact(demandeur, emailReceiver);
		addContact(receiver, emailDemandeur);
		Util.deleteEntity(demande.getKey());

		// ajout de l'évènement dans chacun des utilisateurs en contact.
		Date d = new Date();
		ArrayList<String> usersMail = new ArrayList<String>();
		usersMail.add((String) demandeur.getKey().getName());
		usersMail.add((String) receiver.getKey().getName());
		EventLogger.createEventLogger(demandeur, d,
				EventLogger.EVENT_ADD_CONTACT, usersMail);
		EventLogger.createEventLogger(receiver, d,
				EventLogger.EVENT_ADD_CONTACT, usersMail);

		return true;
	}

	public static boolean refuserDemande(String emailDemandeur,
			String emailReceiver) {
		Entity demande = null;
		for (Entity s : getUserDemandes(emailReceiver)) {
			if (s.getProperty("demandeur").toString()
					.equalsIgnoreCase(emailDemandeur))
				demande = s;
		}
		if (demande == null)
			return false;
		Util.deleteEntity(demande.getKey());
		return true;

	}

	/**
	 * 
	 * @param user
	 *            nécessairement une entité de "Utilisateur"
	 * @return
	 */
	public static String getEmail(Entity user) {
		return user.getKey().getName();
	}

	/**
	 * Retourne l'ensemble des évènements que peut voir user.
	 * 
	 * @param user
	 * @return
	 */
	public static ArrayList<Entity> getEvents(Entity user) {
		ArrayList<Entity> allEvents = new ArrayList<Entity>();
		ArrayList<String> contacts = getAllContacts(user);
		contacts.add((String) user.getKey().getName());
		for (String email : contacts) {
			Entity usrTmp = getUtilisateur(email);
			Iterator<Entity> it = Util.listChildren(
					EventLogger.EVENT_LOGGER_KIND, usrTmp.getKey()).iterator();
			while (it.hasNext()) {
				Entity eToInsert = it.next();
				int i = 0;
				boolean continu = true;
				Date dateToInsert = (Date) eToInsert
						.getProperty(EventLogger.EVENT_LOGGER_DATE);
				while (i < allEvents.size() && continu) {
					Date date = (Date) allEvents.get(i).getProperty(
							EventLogger.EVENT_LOGGER_DATE);
					if (dateToInsert.compareTo(date) <= 0) {
						continu = false;
					} else
						i++;
				}
				allEvents.add(i, eToInsert);
			}
		}
		return allEvents;
	}

	/**
	 * retourne l'ensemble des news classées dans l'ordre chronologique que peut
	 * voir user.
	 * 
	 * @param user
	 * @return
	 */
	public static ArrayList<NewsInfo> getNews(Entity user) {
		ArrayList<Entity> allEvents = getEvents(user);
		ArrayList<NewsInfo> news = new ArrayList<NewsInfo>();
		@SuppressWarnings("unused")
		Entity precEvent = null;
		for (Entity event : allEvents) {
			// DEBUT on enlève les évènements symétriques caractérisés par la même
			// date et les mêmes users impliqués.
			boolean eventToAdd = true;

			ArrayList<String> involvedUserMail = EventLogger
					.listInvolvedUsersMail(event);
			if (involvedUserMail == null) {
				involvedUserMail = new ArrayList<String>();
			}

			if (EventLogger.getOwnerMail(event).equals(
					Utilisateur.getEmail(user))
					|| involvedUserMail.contains(Utilisateur.getEmail(user))) {
				eventToAdd = false;
			}

			if (eventToAdd) {
				for (NewsInfo n : news) {
					if (isTheSameEvent(event, n)) {
						eventToAdd = false;
						break;
					}
				}
			}

			// FIN on enlève les évènements symétriques caractérisés par la même
			// date et les mêmes users impliqués.
			if (eventToAdd) {
				Date date = EventLogger.getDate(event);
				int eventType = EventLogger.getType(event);
				GetInfoServiceImpl gisl = new GetInfoServiceImpl();
				UserInfo ownerInfo = gisl.getUserIdentite(EventLogger
						.getOwnerMail(event));
				ArrayList<String> involvedMails = EventLogger
						.listInvolvedUsersMail(event);
				if (involvedMails == null) {
					involvedMails = new ArrayList<String>();
				}
				ArrayList<UserInfo> users = new ArrayList<UserInfo>();
				for (String mail : involvedMails) {
					users.add(gisl.getUserIdentite(mail));
				}
				NewsInfo newsInfo = new NewsInfo(ownerInfo, eventType, users,
						date);
				news.add(newsInfo);
			}
			precEvent = event;
		}
		return news;
	}

	private static boolean isTheSameEvent(Entity event, NewsInfo n) {
		if (!n.getDate().equals(EventLogger.getDate(event))) {
			return false;
		} else if (n.getEventType() != EventLogger.getType(event)) {
			return false;
		}
		ArrayList<String> eMails = EventLogger.listInvolvedUsersMail(event);
		if (eMails == null) {
			eMails = new ArrayList<String>();
		}
		if (eMails.size() != n.getUsers().size()) {
			return false;
		}
		for (UserInfo nUser : n.getUsers()) {
			if (!eMails.contains(nUser.getEmailAddress())) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Méthode qui retourne une liste de tous les utilisateursMail existants dans
	 * le datastore.
	 * 
	 * @return List<String> de toutes les utilisateursMail existantes.
	 */
	public static List<String> getAllEmail() {

		List<String> ret = new ArrayList<String>();
		Iterable<Entity> results = Util.listEntities("Profil", null, null);

		for (Entity e : results) {
			if (!ret.contains(e.getProperty("email_user").toString()))
				ret.add(e.getProperty("email_user").toString());
		}
		return ret;
	}
	
	/**
	 * Méthode qui retourne une liste de tous les utilisateursNom existants dans
	 * le datastore.
	 * 
	 * @return List<String> de toutes les utilisateursNom existantes.
	 */
	public static List<String> getAllNom() {

		List<String> ret = new ArrayList<String>();
		Iterable<Entity> results = Util.listEntities("Utilisateur", null, null);

		for (Entity e : results) {
			if (!ret.contains(e.getProperty("nom").toString()))
				if (!ret.contains(e.getProperty("prenom").toString()))
					ret.add(e.getProperty("nom").toString()+" "+e.getProperty("prenom").toString());
		}

		return ret;
	}
	
	/**
	 * Méthode qui retourne une liste de toutes les entreprises avec le critère de nom
	 * 
	 * @return toutes les entreprises
	 */
	public static Iterable<Entity> getSocieteByName(String societe) {
		return Util.listEntities("Entreprise", "nom", societe);
	}
	
	/**
	 * Méthode qui retourne une liste de toutes les écoles avec le critère de nom
	 * 
	 * @return toutes les entreprises
	 */
	public static Iterable<Entity> getEcoleByName(String ecole) {
		return Util.listEntities("Ecole", "nom", ecole);
	}
	
	/**
	 * Méthode qui retourne une liste de toutes les spécialités avec le critère de nom
	 * 
	 * @return toutes les entreprises
	 */
	public static Iterable<Entity> getSpecialiteByName(String specialite) {
		return Util.listEntities("Specialite", "nom", specialite);
	}
}
